package com.njcb.ams.store.stable;

import com.google.common.util.concurrent.RateLimiter;
import com.njcb.ams.repository.entity.SysTradeConsole;
import com.njcb.ams.portal.SysBaseDefine;
import com.njcb.ams.support.exception.ExceptionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 并发流量控制器 支持按并发数控制及按单位时间流量控制
 *
 * @author liuyanong 20170224
 *
 */
public class StableControl {

	private static final Logger logger = LoggerFactory.getLogger(StableControl.class);

	private String tradeCode;
	//持有的控制器配置信息
	private SysTradeConsole sysServerConsole;
	private RateLimiter rateLimiter;
	private AtomicLong atomicLong;
	// 最大并发数
	private Integer permitsConNumber;
	private List<Token> tokenColl;

	private StableControl() {
	}

	public static StableControl initInstance(String tradeCode) {
		StableControl instance = new StableControl();
		instance.setTradeCode(tradeCode);
		instance.createConc(SysBaseDefine.DEFAULT_MAX_EXEC);
		instance.createRate(SysBaseDefine.DEFAULT_MAX_RATE);
		return instance;
	}

	/**
	 * 方法功能描述：根据服务控制的配置实例化默认的流量及并发控制器
	 *
	 * @return
	 */
	public StableControl synInstance() {
		this.createConc(sysServerConsole.getMaxExec());
		this.createRate(sysServerConsole.getMaxRate());
		logger.info("交易控制信息: 交易代码[{}],交易名称[{}],最大并发[{}],最大流量[{}],交易状态[{}]", sysServerConsole.getTradeCode(),
				sysServerConsole.getTradeName(), sysServerConsole.getMaxExec(), sysServerConsole.getMaxRate(),
				sysServerConsole.getTradeStatus());
		return this;
	}

	/**
	 * 创建流量控制器，控制最大并发数
	 *
	 * @param permitsConNumber
	 * @return StableControl
	 */
	public StableControl createConc(Integer permitsConNumber) {
		this.atomicLong = new AtomicLong();
		this.permitsConNumber = permitsConNumber;
		return this;
	}

	/**
	 * 创建流量控制器，控制最大并发数
	 *
	 * @param permitsConNumber
	 *            最大并发数
	 * @param timeout
	 *            令牌超时时间
	 * @return StableControl
	 */
	public StableControl createConc(Integer permitsConNumber, Long timeout) {
		logger.info(tradeCode + "交易的最大流量数:" + permitsConNumber + ",超时时间:" + timeout);
		this.atomicLong = new AtomicLong();
		this.permitsConNumber = permitsConNumber;
		for (int i = 0; i < permitsConNumber; i++) {
			Token token = new Token();
			this.tokenColl.add(token);
		}
		return this;
	}

	/**
	 * 创建流量控制器，控制单位时间流量
	 *
	 * @param permitsPerSecond
	 * @return StableControl
	 */
	public StableControl createRate(Integer permitsPerSecond) {
		if (permitsPerSecond < 1) {
			ExceptionUtil.throwAppException("流量速率必须大于零");
		}
		this.rateLimiter = RateLimiter.create(permitsPerSecond);
		return this;
	}

	/**
	 * 调整流量
	 *
	 * @param permitsPerSecond
	 */
	public void setRate(Integer permitsPerSecond) {
		rateLimiter.setRate(permitsPerSecond);
	}

	/**
	 * 调整
	 *
	 * @param permitsPerSecond
	 */
	public void setConc(Integer permitsPerSecond) {
	}

	/**
	 * @param sysServerConsole
	 *            the sysServerConsole to set
	 */
	public void setSysServerConsole(SysTradeConsole sysServerConsole) {
		this.sysServerConsole = sysServerConsole;
	}

	/**
	 * @return the sysServerConsole
	 */
	public SysTradeConsole getSysServerConsole() {
		return sysServerConsole;
	}

	/**
	 * 获取流量令牌
	 */
	public void tryAcquireRate() {
		if (rateLimiter.tryAcquire()) {
			logger.debug("获取令牌成功");
		} else {
			logger.debug("获取令牌失败");
			ExceptionUtil.throwAppException("超最大流量[" + rateLimiter.getRate() + "]限制");
		}
	}

	/**
	 * 获取并发令牌
	 */
	public void tryAcquireConc() {
		if (atomicLong.incrementAndGet() > permitsConNumber) {
			ExceptionUtil.throwAppException("超最大并发[" + permitsConNumber + "]限制  ");
		}
	}

	/**
	 * 归还令牌
	 */
	public void release() {
		atomicLong.decrementAndGet();
	}


	public void setTradeCode(String tradeCode) {
		this.tradeCode = tradeCode;
	}

	public String getTradeCode() {
		return tradeCode;
	}

}
